package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.DrawbackStatusEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.NumberConvertUtil;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.scm.dto.finance.*;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.finance.Drawback;
import com.tsfyun.scm.entity.finance.DrawbackOrder;
import com.tsfyun.scm.entity.order.ExpOrder;
import com.tsfyun.scm.entity.system.Subject;
import com.tsfyun.scm.entity.system.SubjectBank;
import com.tsfyun.scm.entity.system.SubjectOverseas;
import com.tsfyun.scm.entity.system.SubjectOverseasBank;
import com.tsfyun.scm.enums.SubjectTypeEnum;
import com.tsfyun.scm.mapper.finance.DrawbackMapper;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.finance.IDrawbackOrderService;
import com.tsfyun.scm.service.finance.IDrawbackService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.finance.IPurchaseContractService;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.*;
import com.tsfyun.scm.vo.finance.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 退税单 服务实现类
 * </p>
 *
 *
 * @since 2021-10-20
 */
@Service
public class DrawbackServiceImpl extends ServiceImpl<Drawback> implements IDrawbackService {

    @Autowired
    private DrawbackMapper drawbackMapper;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private IPurchaseContractService purchaseContractService;
    @Autowired
    private IDrawbackOrderService drawbackOrderService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private IExpOrderService expOrderService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICommonService commonService;
    @Autowired
    private ISubjectOverseasService subjectOverseasService;
    @Autowired
    private ISubjectOverseasBankService subjectOverseasBankService;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private ISubjectBankService subjectBankService;

    @Override
    public PageInfo<DrawbackVO> pageList(DrawbackQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        List<DrawbackVO> list = drawbackMapper.list(params);
        return new PageInfo<>(list);
    }

    @Override
    public DrawbackPlusVO detail(Long id, String operation) {
        Drawback drawback = super.getById(id);
        Optional.ofNullable(drawback).orElseThrow(()->new ServiceException("退税单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation),drawback.getStatusId());
        DrawbackVO drawbackVO = beanMapper.map(drawback,DrawbackVO.class);
        Customer customer = customerService.getById(drawback.getCustomerId());
        drawbackVO.setCustomerName(customer.getName());
        DrawbackPlusVO plusVO = new DrawbackPlusVO();
        plusVO.setDrawback(drawbackVO);
        plusVO.setOrders(drawbackOrderService.findOrderByDrawbackId(drawback.getId()));
        return plusVO;
    }

    @Override
    public CreateDrawbackPlusVO recalculate(DrawbackDTO dto) {
        CreateDrawbackPlusVO drawbackPlusVO = purchaseContractService.initDrawback(StringUtils.stringToLongs(dto.getPurchaseId()),dto.getBillie());
        drawbackPlusVO.getDrawback().setClaimPaymentDate(dto.getClaimPaymentDate());
        drawbackPlusVO.getDrawback().setPayeeName(dto.getPayeeName());
        drawbackPlusVO.getDrawback().setPayeeBank(dto.getPayeeBank());
        drawbackPlusVO.getDrawback().setPayeeBankNo(dto.getPayeeBankNo());
        drawbackPlusVO.getDrawback().setBillie(dto.getBillie());
        return drawbackPlusVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveDrawback(DrawbackDTO dto) {
        // 初始化退税单
        CreateDrawbackPlusVO drawbackPlusVO = purchaseContractService.initDrawback(StringUtils.stringToLongs(dto.getPurchaseId()),dto.getBillie());
        Drawback drawback = new Drawback();
        CreateDrawbackVO drawbackVO = drawbackPlusVO.getDrawback();
        drawback.setCustomerId(drawbackVO.getCustomerId());
        drawback.setPurchaseNo(drawbackVO.getPurchaseNo());
        drawback.setOrderNo(drawbackVO.getOrderNo());
        drawback.setClientNo(drawbackVO.getClientNo());
        drawback.setAccountValue(drawbackVO.getAccountValue());
        DrawbackStatusEnum statusEnum = DrawbackStatusEnum.WAIT_CONFIRM;
        drawback.setStatusId(statusEnum.getCode());
        drawback.setClaimPaymentDate(dto.getClaimPaymentDate());
        drawback.setPayeeName(dto.getPayeeName());
        drawback.setPayeeBank(dto.getPayeeBank());
        drawback.setPayeeBankNo(dto.getPayeeBankNo());
        drawback.setMemo(dto.getMemo());
        drawback.setBillie(drawbackVO.getBillie());
        if(drawback.getAccountValue().compareTo(BigDecimal.ZERO) != 1){
            throw new ServiceException("退税总额必须大于零");
        }
        String docNo = serialNumberService.generateDocNo(SerialNumberTypeEnum.DRAWBACK);
        drawback.setDocNo(docNo);
        try {
            super.saveNonNull(drawback);
        } catch (DuplicateKeyException e) {
            if (e.getMessage().contains("UK_drawback_doc_no")) {
                throw new ServiceException("退税单号已经存在，请稍后重试");
            } else {
                throw new ServiceException("保存失败，请检查填写数据是否合法");
            }
        }
        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.DRAWBACK_CREATE,
                drawback.getId().toString(), Drawback.class.getName(),
                statusEnum.getCode(),statusEnum.getName(),"退税申请");
        // 保存退税明细
        List<DrawbackOrder> drawbackOrderList = new ArrayList<>();
        List<DrawbackOrderVO> orders = drawbackPlusVO.getOrders();
        for(DrawbackOrderVO orderVO : orders){
            DrawbackOrder drawbackOrder = new DrawbackOrder();
            drawbackOrder.setId(snowflake.nextId());
            drawbackOrder.setDrawbackId(drawback.getId());
            drawbackOrder.setPurchaseId(orderVO.getPurchaseId());
            drawbackOrder.setOrderId(orderVO.getOrderId());
            drawbackOrder.setAgencyFee(orderVO.getAgencyFee());
            drawbackOrder.setDeductionValue(orderVO.getDeductionValue());
            drawbackOrder.setDrawbackValue(orderVO.getDrawbackValue());
            drawbackOrder.setIsSettleAccount(orderVO.getIsSettleAccount());
            drawbackOrder.setSettlementValue(orderVO.getSettlementValue());
            drawbackOrder.setEstimatedPayment(orderVO.getEstimatedPayment());
            drawbackOrder.setActualDrawbackValue(orderVO.getActualDrawbackValue());
            drawbackOrder.setAlreadyDrawbackValue(orderVO.getAlreadyDrawbackValue());
            drawbackOrderList.add(drawbackOrder);
            ExpOrder expOrder = expOrderService.getById(drawbackOrder.getOrderId());
            // 修改订单已退税金额
            expOrder.setAlreadyDrawbackValue(expOrder.getAlreadyDrawbackValue().add(drawbackOrder.getDrawbackValue()).setScale(2, BigDecimal.ROUND_HALF_UP));
            // 写入退税单号
            List<String> drawbackNos = new ArrayList<>();
            if(StringUtils.isNotEmpty(expOrder.getDrawbackNo())){
                drawbackNos = new ArrayList<>(Arrays.asList(StringUtils.removeSpecialSymbol(expOrder.getDrawbackNo()).split(",")));
            }
            if(!drawbackNos.contains(drawback.getDocNo())){
                drawbackNos.add(drawback.getDocNo());
            }
            expOrder.setDrawbackNo(String.join(",",drawbackNos));
            expOrderService.updateById(expOrder);
        }
        drawbackOrderService.savaBatch(drawbackOrderList);

        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.DRAWBACK.getCode());
        taskNoticeContentDTO.setDocumentId(drawback.getId().toString());
        taskNoticeContentDTO.setCustomerId(drawback.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.DRAWBACK_CONFIRM.getCode());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口退税单【%s】需要您确认。",drawbackVO.getCustomerName(),drawback.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",drawback.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeDrawback(Long id) {
        Drawback drawback = super.getById(id);
        Optional.ofNullable(drawback).orElseThrow(()->new ServiceException("退税单不存在"));
        List<DrawbackOrder> drawbackOrderList = drawbackOrderService.findByDrawbackId(drawback.getId());
        drawbackOrderList.forEach(drawbackOrder -> {
            // 修改订单已退税金额
            ExpOrder expOrder = expOrderService.getById(drawbackOrder.getOrderId());
            expOrder.setAlreadyDrawbackValue(expOrder.getAlreadyDrawbackValue().subtract(drawbackOrder.getDrawbackValue()).setScale(2, BigDecimal.ROUND_HALF_UP));
            List<String> drawbackNos = new ArrayList<>(Arrays.asList(StringUtils.removeSpecialSymbol(expOrder.getDrawbackNo()).split(",")));
            for(int i=0;i<drawbackNos.size();i++){
                if(drawback.getDocNo().equals(drawbackNos.get(i))){
                    drawbackNos.remove(i);
                }
            }
            expOrder.setDrawbackNo(String.join(",",drawbackNos));
            expOrderService.updateById(expOrder);
            drawbackOrderService.removeById(drawbackOrder.getId());
        });
        //删除历史状态
        statusHistoryService.removeHistory(id.toString(), Drawback.class.getName());
        //清空任务通知
        clearTaskNotice(id);
        super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirm(TaskDTO dto) {
        Drawback drawback = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(drawback.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.DRAWBACK.getCode());
        taskNoticeContentDTO.setDocumentId(drawback.getId().toString());
        taskNoticeContentDTO.setCustomerId(drawback.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.DRAWBACK_BANK.getCode());
        Customer customer = customerService.getById(drawback.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口退税单【%s】请您尽快确定付款行。",customer.getName(),drawback.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",drawback.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backConfirm(TaskDTO dto) {
        Drawback drawback = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(drawback.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.DRAWBACK.getCode());
        taskNoticeContentDTO.setDocumentId(drawback.getId().toString());
        taskNoticeContentDTO.setCustomerId(drawback.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.DRAWBACK_CONFIRM.getCode());
        Customer customer = customerService.getById(drawback.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口退税单【%s】已被退回：%s",customer.getName(),drawback.getDocNo(),dto.getMemo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",drawback.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backConfirmBank(TaskDTO dto) {
        Drawback drawback = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(drawback.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.DRAWBACK.getCode());
        taskNoticeContentDTO.setDocumentId(drawback.getId().toString());
        taskNoticeContentDTO.setCustomerId(drawback.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.DRAWBACK_BANK.getCode());
        Customer customer = customerService.getById(drawback.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口退税单【%s】需要您重新确定付款行。",customer.getName(),drawback.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",drawback.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmBank(ExpConfirmBankDTO dto) {
        Drawback drawback = super.getById(dto.getId());
        Optional.ofNullable(drawback).orElseThrow(()->new ServiceException("退税单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.DRAWBACK_BANK,drawback.getStatusId());
        SubjectTypeEnum subjectType = SubjectTypeEnum.of(dto.getSubjectType());
        drawback.setSubjectId(dto.getSubjectId());
        switch (subjectType){
            case ABROAD:
                // 境外主体
                SubjectOverseas subjectOverseas = subjectOverseasService.getById(drawback.getSubjectId());
                Optional.ofNullable(subjectOverseas).orElseThrow(()->new ServiceException("境外付款主体不存在"));
                drawback.setSubjectName(subjectOverseas.getName());
                // 境外银行
                SubjectOverseasBank subjectOverseasBank = subjectOverseasBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectOverseasBank).orElseThrow(()->new ServiceException("境外付款银行不存在"));
                drawback.setSubjectBank(subjectOverseasBank.getName());
                drawback.setSubjectBankNo(subjectOverseasBank.getAccount());
                break;
            case MAINLAND:
                // 境内主体
                Subject subject = subjectService.getById(drawback.getSubjectId());
                Optional.ofNullable(subject).orElseThrow(()->new ServiceException("境内付款主体不存在"));
                drawback.setSubjectName(subject.getName());
                // 境内银行
                SubjectBank subjectBank = subjectBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectBank).orElseThrow(()->new ServiceException("境内付款银行不存在"));
                drawback.setSubjectBank(subjectBank.getName());
                drawback.setSubjectBankNo(subjectBank.getAccount());
                break;

            default:
                throw new ServiceException("付款主体类型错误");
        }
        drawback.setSubjectType(subjectType.getCode());
        DrawbackStatusEnum historyStatus = DrawbackStatusEnum.of(drawback.getStatusId());
        DrawbackStatusEnum statusEnum = DrawbackStatusEnum.WAIT_PAY;
        drawback.setStatusId(statusEnum.getCode());
        super.updateById(drawback);

        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.DRAWBACK_BANK,
                drawback.getId().toString(), Drawback.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                statusEnum.getCode(),statusEnum.getName(),"确定付款银行");
        // 清空任务通知
        clearTaskNotice(drawback.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.DRAWBACK.getCode());
        taskNoticeContentDTO.setDocumentId(drawback.getId().toString());
        taskNoticeContentDTO.setCustomerId(drawback.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("出口退税单【%s】已确定付款行，请尽快完成付款。", drawback.getDocNo()));
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.DRAWBACK_COMPLETED.getCode());
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",drawback.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paymentCompleted(ExpPaymentCompletedDTO dto) {
        Drawback drawback = super.getById(dto.getId());
        Optional.ofNullable(drawback).orElseThrow(()->new ServiceException("退税单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.DRAWBACK_COMPLETED,drawback.getStatusId());
        drawback.setActualPaymentDate(dto.getActualPaymentDate());
        DrawbackStatusEnum historyStatus = DrawbackStatusEnum.of(drawback.getStatusId());
        DrawbackStatusEnum statusEnum = DrawbackStatusEnum.COMPLETED;
        drawback.setStatusId(statusEnum.getCode());
        super.updateById(drawback);

        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_COMPLETED,
                drawback.getId().toString(), Drawback.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                statusEnum.getCode(),statusEnum.getName(),"确定付款完成");

        // 清空任务通知
        clearTaskNotice(drawback.getId());
    }

    @Override
    public PrintDrawBackVO getPaymentPrintData(Long id) {
        Drawback drawback = super.getById(id);
        Optional.ofNullable(drawback).orElseThrow(()->new ServiceException("退税信息不存在"));
        PrintDrawBackVO printDrawBackVO = new PrintDrawBackVO();
        printDrawBackVO.setPayeeName(drawback.getPayeeName());
        printDrawBackVO.setClaimPaymentDate(drawback.getClaimPaymentDate());
        printDrawBackVO.setApplyPaymentDate(new Date());
        printDrawBackVO.setAccountValue(drawback.getAccountValue());
        printDrawBackVO.setAccountValueCn(NumberConvertUtil.number2Chinese(drawback.getAccountValue().toString()));
        printDrawBackVO.setPayeeBank(drawback.getPayeeBank());
        printDrawBackVO.setPayeeBankNo(drawback.getPayeeBankNo());
        printDrawBackVO.setApplyUser(SecurityUtil.getCurrentPersonName());
        printDrawBackVO.setClientNo(drawback.getClientNo());
        return printDrawBackVO;
    }

    //清空任务通知
    public void clearTaskNotice(Long id){
        taskNoticeContentService.deleteTaskNotice(DomainTypeEnum.DRAWBACK.getCode(), id, "");
    }
}
